<template>
    <div class="main">
        <div ref="sceneNode" class="scene">
        </div>
        <div class="control">
            <span>播放动画</span>
            <input type="checkbox" name="animate" v-model="autoPlayAnimate">
            <br>
            <select v-model="nowAnimate">
                <option v-for="item in animations" :value="item.name">{{ item.name }}</option>
            </select>
            <br>
            <span>倍速</span>
            <input type="range" :min="1" :max="2.5" :step="0.5" v-model="speed" />
            <br>
            <span>金属度</span>
            <input type="range" :min="0" :max="1" :step="0.1" v-model="metalness" />
            <br>
            <span>粗糙度</span>
            <input type="range" :min="0" :max="1" :step="0.1" v-model="roughness" />
        </div>
    </div>
</template>

<script setup name="ModelView">
/**
 * @author zhaojiuyi
 * @date 2023/6/22
 * */
import {
    AxesHelper,
    AmbientLight,
    Fog, GridHelper,
    Mesh, Clock, Scene,
    MeshStandardMaterial,
    WebGLRenderer, AnimationMixer,
    PerspectiveCamera, PlaneGeometry, PointLight, Color, BoxGeometry, MeshPhysicalMaterial, PointLightHelper
} from "three"
import {OrbitControls} from 'three/addons/controls/OrbitControls.js';
import {onMounted, onUnmounted, ref, watchEffect} from "vue";
import {GLTFLoader} from "three/examples/jsm/loaders/GLTFLoader";
import {LightProbeHelper} from "three/addons/helpers/LightProbeHelper";

let timer = null
let animationMixer = null
let gblModel = null
const meshList = []

const metalness = ref(0) // 金属度
const roughness = ref(0) // 粗糙度

const autoPlayAnimate = ref(false)
const nowAnimate = ref(null)
const animations = ref([])
const sceneNode = ref()
const speed = ref(1)
// 渲染器
const renderer = new WebGLRenderer({antialias: true})
renderer.shadowMap.enabled = true

watchEffect(() => {
    animations.value.forEach(item => item.animate.stop())
    const animate = animations.value.filter(item => item.name === nowAnimate.value)
    if (animate.length){
        animate[0].animate.play()
    }
})
watchEffect(()=>{
    const scale = speed.value
    if (!animationMixer) return
    animationMixer.timeScale = scale
})
watchEffect(()=>{
    const metalnessTemp = metalness.value
    const roughnessTemp = roughness.value
    if (meshList.length){
        console.log(metalnessTemp,roughnessTemp)
        meshList.forEach(item =>{
            item.material.metalness = metalnessTemp
            item.material.roughness = roughnessTemp
        })
    }
})

// 场景
const scene = new Scene()
scene.fog = new Fog(0xcccccc, 5, 100);

// 相机
const camera = new PerspectiveCamera()
camera.position.set(5, 5, 5)

// 坐标轴
const axesHelper = new AxesHelper(1000)
scene.add(axesHelper)

// 环境光
const ambientLight = new AmbientLight(0xffffff, 1); // 柔和的白光
scene.add(ambientLight);

// 点光源
const pointLight = new PointLight(0xffffff, 5, 100);
pointLight.position.set(0, 10, 10);
pointLight.castShadow = true
pointLight.shadow.mapSize.width = 5000
pointLight.shadow.mapSize.height = 5000
pointLight.shadow.camera.near = 0.5
pointLight.shadow.camera.far = 5
scene.add(pointLight);

const helper = new PointLightHelper( pointLight, 1 );
scene.add( helper );


// 地面
const geometry = new PlaneGeometry(500, 500, 10, 10);
const material = new MeshStandardMaterial({color: 0x6e6e6e});
const circle = new Mesh(geometry, material);
circle.rotation.x = -Math.PI / 2.0;
circle.receiveShadow = true
scene.add(circle);

// 地面网格
const gridHelper = new GridHelper(500, 200);
scene.add(gridHelper);

// 轨道控制器
const controls = new OrbitControls(camera, renderer.domElement)

// 盒子
const boxGeometry = new BoxGeometry(1,1,1)
const standMaterial = new MeshPhysicalMaterial({color: new Color("gray")})
const cube = new Mesh(boxGeometry,standMaterial)
cube.position.set(-1,0.5,0)
cube.castShadow = true
cube.receiveShadow = true
cube.emissive = 0xffffff
cube.material.metalness = .8
cube.material.roughness = .4
scene.add(cube)

// 时钟
const clock = new Clock()

const render = () => {
    renderer.render(scene, camera)
    controls.update();
    if (animationMixer&&autoPlayAnimate.value) {
        animationMixer.update(clock.getDelta())
    }
    timer = requestAnimationFrame(render)
}

const createModel = () => {
    const loader = new GLTFLoader()
    return new Promise((resolve, reject) => {
        loader.load('/models/Michelle.glb', glb => {
                resolve(glb)
            }, () => {
            },
            err => {
                reject()
            })
    })
}

onMounted(async () => {
    const sceneDom = sceneNode.value
    renderer.setSize(sceneDom.clientWidth, sceneDom.clientHeight)
    sceneDom.innerHTML = null
    sceneDom.appendChild(renderer.domElement)

    gblModel = await createModel()
    console.log(gblModel.scene)
    animationMixer = new AnimationMixer(gblModel.scene)
    gblModel.animations.forEach(animate => {
        animations.value.push({
            name: animate.name,
            animate: animationMixer.clipAction(animate)
        })
    })
    animations.value[0].animate.play()
    gblModel.scene.traverse(e => {
        if (e.isMesh) {
            meshList.push(e)
            console.log(e.metalness)
            metalness.value = e.material.metalness
            roughness.value = e.material.roughness
            e.castShadow = true
        }
    })
    scene.add(gblModel.scene)
    render()
})

onUnmounted(() => {
    cancelAnimationFrame(timer)
})


</script>
<style>
.main {
    width: 100vw;
    height: 100vh;
    overflow: hidden;
    display: flex;

}

.scene {
    flex-grow: 3;
    flex-shrink: 0;
    flex-basis: 0;
}

.control {
    flex-grow: 1;
    flex-shrink: 0;
    flex-basis: 0;
}
</style>

